package com.fept.msgcore.msgserver.netty.handler;

import com.fept.msgcore.msgserver.common.ReplyDataBean;
import com.fept.msgcore.msgserver.common.ServerInnerSettings;
import com.fept.msgcore.msgserver.netty.channel.ChannelManager;
import com.fept.msgcore.msgserver.protocol.frame.Frame;
import com.fept.msgcore.msgserver.protocol.model.header.Header;
import com.fept.msgcore.msgserver.protocol.model.base.ProtocolConstants;
import com.fept.msgcore.msgserver.protocol.model.body.BaseBody;
import com.fept.msgcore.msgserver.protocol.model.body.DataBody;
import com.fept.msgcore.msgserver.protocol.model.message.MessageModel;
import com.fept.msgcore.msgserver.protocol.model.option.AnswerOption;
import com.fept.msgcore.msgserver.protocol.model.option.BaseOption;
import com.fept.msgcore.msgserver.protocol.model.option.EncryptOption;
import com.fept.msgcore.msgserver.protocol.model.option.SignOption;
import com.fept.msgcore.msgserver.protocol.util.FrameHelper;
import com.fept.msgcore.msgserver.senderinfo.Client;
import com.fept.msgcore.msgserver.senderinfo.ClientManager;
import com.fept.msgcore.msgserver.util.json.JSONUtil;
import com.fept.msgcore.repository.pojo.Device;
import com.fept.msgcore.repository.pojo.ReportRecord;
import com.fept.msgcore.repository.serviceImpl.DeviceServiceImpl;
import com.fept.msgcore.repository.serviceImpl.ReportRecordServiceImpl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by Srma on 1/13/2017.
 */
public class ProtocolMessageHandler extends BaseMessageHandler {

    private static Logger logger = LoggerFactory.getLogger(ProtocolMessageHandler.class);

    private static ProtocolMessageHandler instance = null;

    private ClientManager clientManager;
    private Client client;


    /**
     *
     * @param name
     */
    public ProtocolMessageHandler(String name) {
        super(name);

        try {
            this.setReadTimeout(ServerInnerSettings.getClientReadTimeOut());
            this.setWriteTimeout(ServerInnerSettings.getClientWriteTimeOut());
        } catch (Exception e) {
            logger.error("failed to set read and write time out value for protocol channels");
            logger.error(e.getMessage());
        }
        this.clientManager = ClientManager.getClientManager(name);

        this.instance = this;
    }

    @Override
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {

        InetAddress hostIPAddress = ((InetSocketAddress) ctx.channel()
                .remoteAddress()).getAddress();
        String hostIP = hostIPAddress.getHostAddress();
        int hostPort = ((InetSocketAddress) ctx.channel().remoteAddress())
                .getPort();

        try {

            if (this.clientManager.getNumberOfConcurrentClients() >= ServerInnerSettings
                    .getMaxConcurrentClients()) {
                ctx.close();
                logger.error("Too many concurrent clients!");
            }



            /**
             * get old channel
             */
            Channel channel = ChannelManager.getChannelManager(this.getName())
                    .getChannel(hostIP);
            if (channel != null) {
                InetAddress oldHostIPAddress = ((InetSocketAddress) ctx
                        .channel().remoteAddress()).getAddress();
                String oldHostIp = oldHostIPAddress.getHostAddress();
                int oldHostPort = ((InetSocketAddress) channel.remoteAddress())
                        .getPort();

                logger.error(String
                        .format("channel already registered, client old ip = %s, old port = %d!",
                                oldHostIp, oldHostPort));
                ChannelManager.getChannelManager(this.getName()).removeChannel(
                        hostIP);
                channel.close();
            }

            /**
             * get old client object
             */
            Client client = this.clientManager.getClient(hostIP,hostPort);
            if (client != null) {
                logger.error(String.format(
                        "client already register, client ip = %s, port = %d!",
                        client.getIp(), client.getPort()));
                this.clientManager.removeClient(hostIP+":"+hostPort);
            }

            client = new Client();
            synchronized (client) {
                if (this.client != null) {
                    logger.warn("client has been set: " + this.client);
                }
                this.client = client;
                client.setIp(hostIP);
                client.setPort(hostPort);
                this.clientManager.addClient(client);
                //设置通道超时时间  30秒     		this is a test
//				ctx.channel().pipeline().addLast("readTimeoutHandler", new ReadTimeoutHandler(30));

                ChannelManager.getChannelManager(this.getName()).addChannel(
                        hostIP+":"+hostPort, ctx.channel());



                logger.info(String.format(
                        "%s channel active, client ip = %s, port = %d!",
                        this.getName(), hostIP, hostPort));
            }

        } catch (Exception e) {
            logger.error(String.format("exception on channel active: %s", e));
            logger.error(e.getMessage());
            ctx.close();
        }
    }

    @Override
    public void channelInactive(final ChannelHandlerContext ctx) {

        InetAddress hostIPAddress = ((InetSocketAddress) ctx.channel()
                .remoteAddress()).getAddress();
        String hostIP = hostIPAddress.getHostAddress();
        int hostPort = ((InetSocketAddress) ctx.channel().remoteAddress())
                .getPort();
        try {
            logger.info(String.format(
                    "%s Class:ProtocolMessageHandler channel inactive, client ip = %s, port = %d!",
                    this.getName(), hostIP, hostPort));

            Client c = this.clientManager.getClient(hostIP,hostPort);
            if (c == null) {
                // no such client found, something is wrong
                logger.info(String.format(
                        "client ip = %s, port = %d, isn't registered!", hostIP,
                        hostPort));
            } else {
                synchronized (c) {
                    if (hostPort == c.getPort()) {
                        this.clientManager.removeClient(hostIP+":"+hostPort);
                        Channel channel = ChannelManager.getChannelManager(
                                this.getName()).getChannel(hostIP);
                        if (channel != null) {
                            channel.close();
                        }
                        ChannelManager.getChannelManager(this.getName())
                                .removeChannel(hostIP);
                        logger.info(String.format(
                                "Remove client ip = %s, port = %s!", hostIP,
                                hostPort));
                    }
                }
            }

            this.client = null;
        } catch (Exception e) {
            logger.error(String.format("exception on channel inactive: %s", e));
            logger.error(e.getMessage());
        }
    }

    @Override
    public void messageReceived(ChannelHandlerContext ctx, Object req) throws Exception
    {
        System.out.println("0+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++messageReceived!"+new Date());
        try {
            InetAddress hostIPAddress = ((InetSocketAddress) ctx.channel()
                    .remoteAddress()).getAddress();
            String hostIP = hostIPAddress.getHostAddress();
            int hostPort = ((InetSocketAddress) ctx.channel().remoteAddress())
                    .getPort();

            Channel ch = ctx.channel();

            if (req == null || !(req instanceof Frame)) {
                logger.error("Request ins't frame object!");
//				ctx.channel().close();
                return;
            }

            Frame msg = (Frame) req;

            /**
             * 成功接收帧，更新发送机缓存
             */
            //SenderInfoManager.getInstance().updateSenderInfoModel(msg.getHeader().getSenderId(), hostIP,hostPort);

            List<Frame> replies = new ArrayList<Frame>();
            replies.addAll(this.processFrame(msg));
            msg.setProcessedTime(System.currentTimeMillis());

            logger.debug("" + msg);

            for (Frame r : replies) {
                if (r != null) {
                    ch.write(r);

                }
            }



        } catch (Exception e) {
            logger.error(String.format("exception on message received: %s", e));
            logger.error(e.getMessage());
        }

    }

    private List<Frame> processFrame(Frame msg) {
        switch (msg.getErrorCode()) {
            case SUCCESS:
                return processFrameSuccess(msg);
            case SIGN_VALIDATION_ERROR:
                return this.processFrameInvalid(msg,
                        ProtocolConstants.ANSWER_OPTION_SIGN_ERROR);
            case DECRYPTION_ERROR:
                return this.processFrameInvalid(msg,
                        ProtocolConstants.ANSWER_OPTION_ENCRYPT_ERROR);
            default:
                return this.processFrameInvalid(msg,
                        ProtocolConstants.ANSWER_OPTION_FORMAT_ERROR);
        }
    }

    public void processData(MessageModel messageModel)
    {
        ReportRecord record = new ReportRecord();
        record.setPort(messageModel.getPort() + "");
        record.setIp(messageModel.getIp());

        Map<String, String> data_map = JSONUtil.jsonToMap(messageModel.getParams());

        record.setDeviceId(1);
        record.setBatteryValude(new BigDecimal(data_map.get("bv")));
        record.setFlowSwitch(Integer.parseInt(data_map.get("fs")));
        record.setLeanSwitch(Integer.parseInt(data_map.get("ls")));
        record.setPressureValue(new BigDecimal(data_map.get("pv")));
        record.setPressureSwitch(Integer.parseInt(data_map.get("ps")));
        record.setLat(new BigDecimal(data_map.get("la")));
        record.setLon(new BigDecimal(data_map.get("lo")));
        record.setReedSwitch(Integer.parseInt(data_map.get("rs")));
        record.setTemperatureValude(new BigDecimal(data_map.get("tv")));
        record.setRssi(Integer.parseInt(data_map.get("rssi")));
        record.setVibrationSwitch(Integer.parseInt(data_map.get("is")));
        record.setIccid(data_map.get("iccid"));
        record.setStatus(0);
        record.setStatusMsg("正常");
        record.setType(0);

        record.setReportTime(new Date());

//        System.out.println("9>>>>>>save record....");
        //ReportRecordServiceImpl.getInstance().addReportRecord(record);

        Device device = DeviceServiceImpl.getInstance().findByDeviceNo(data_map.get("id"));

        if(device == null)
        {
            logger.info("device is null");
        }
        else
        {
            logger.info("get device" +device.getId()+"");
        }


    }

    /**
     *
     * @param msg
     * @param invalidType
     * @return
     */
    private List<Frame> processFrameInvalid(Frame msg, int invalidType) {
        logger.error(String
                .format("processing invalid frame: id = %d, senderId = %d, invalidType = %d",
                        msg.getHeader().getId(), msg.getHeader().getSenderId(),
                        invalidType));
        List<Frame> replies = new ArrayList<Frame>();
        Frame reply = new Frame();
        reply.setHeader(this.createReplyFrameHeaderByFrame(msg));

        AnswerOption answerOption = AnswerOption.getAnswerOption(invalidType);
        reply.getOptions().add(answerOption);

        replies.add(reply);
        return replies;
    }


    /**
     *
     * @param msg
     * @return
     */
    private List<Frame> processFrameSuccess(Frame msg) {
        List<Frame> replies = new ArrayList<Frame>();

//        /**
//         * 校验时间漂移
//         */
//        if (Math.abs(System.currentTimeMillis()
//                - msg.getHeader().getTimestamp()) > MsgServerSettings
//                .getInstance().getMsgServerSettingsBean()
//                .getTimeDriftInterval()) {
//            return this.processFrameInvalid(msg,
//                    ProtocolConstants.ANSWER_OPTION_TIME_ERROR);
////			System.out.println("--------------------ANSWER_OPTION_TIME_ERROR DETECTED.  TIME a-b="+Math.abs(System.currentTimeMillis()
////					- msg.getHeader().getTimestamp())+", and TimeDriftInterval setting is :"+MsgServerSettings.getInstance().getMsgServerSettingsBean().getTimeDriftInterval());
//        }

        boolean signOptionPass = false, encryptOptionPass = false;
        /**
         * 处理选项
         */
        for (BaseOption option : msg.getOptions()) {System.out.println("1=== frame:+"+msg.getHeader().getId()+",option type:"+option.getType());
            switch (option.getType()) {
                case ProtocolConstants.OPTION_NOP:
                    /**
                     * 帧为空帧，不需要再应答
                     */
                    System.out.println("--------NOP! "+new Date());


                    return replies;
                case ProtocolConstants.OPTION_ANSWER:
                    /**
                     * 存在应答选项，帧为应答帧，不需要再应答
                     */
                    System.out.println("--------GET REPLY! "+new Date());
                    return replies;
                case ProtocolConstants.OPTION_SIGN:
                    signOptionPass = (((EncryptOption) option).getAlgorithm() != SignOption.NONE);
                    break;
                case ProtocolConstants.OPTION_ENCRYPT:
                    encryptOptionPass = (((EncryptOption) option).getAlgorithm() != EncryptOption.NONE);System.out.println("2>>>>>>>>>>>>>>EncryptOption.getAlgorithm:"+((EncryptOption) option).getAlgorithm());
                    break;
            }
        }

        /**
         * 签名选项未通过检查，返回签名错误
         */
//		if ((!signOptionPass)
//				&& (!MsgServerSettings.getInstance().getMsgServerSettingsBean()
//						.isSignOptionOptional())) {System.out.println("3>>>>>>ANSWER_OPTION_SIGN_ERROR");
//			this.processFrameInvalid(msg,
//					ProtocolConstants.ANSWER_OPTION_SIGN_ERROR);
//		}

        /**
         * 加密选项未通过检查，返回加密错误
         */
//		if ((!encryptOptionPass)
//				&& (!MsgServerSettings.getInstance().getMsgServerSettingsBean()
//						.isEncryptOptionOptional())) {System.out.println("4>>>>>>ANSWER_OPTION_ENCRYPT_ERROR");
//			this.processFrameInvalid(msg,
//					ProtocolConstants.ANSWER_OPTION_ENCRYPT_ERROR);
//		}

        /**
         * 帧正常接收处理，设置响应应答帧
         */
        Frame reply = new Frame();
        reply.setHeader(this.createReplyFrameHeaderByFrame(msg));

        AnswerOption answerOption = AnswerOption
                .getAnswerOption(ProtocolConstants.ANSWER_OPTION_OK);
        reply.getOptions().add(answerOption);

        DataBody dataBody = new DataBody();
        ReplyDataBean data = new ReplyDataBean();
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE, +5); //得到前一天
        Date date = calendar.getTime();
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        data.setNrt(df.format(date));
        data.setOff("0");
        data.setWt(df.format(date));
        dataBody.setEventJson(JSONUtil.beanToJson(data));
        reply.getBodies().add(dataBody);

        replies.add(reply);
        System.out.println("5>>>>>>replies add one reply");



        /**
         * 开始逐个处理帧体
         */
        for (BaseBody body : msg.getBodies()) {
            MessageModel messageModel = FrameHelper
                    .convertMessageBodyToMessageModel(msg, body);

            if (messageModel != null) {System.out.println("6>>>>>>messageModel is not null:"+messageModel);
                //FrameEventTaskThread.addFrameEvent(messageModel);
            }else{
                System.out.println("7>>>>>>messageModel is null");
            }

            switch (body.getType()) {
//                case ProtocolConstants.BODY_COMMAND:
//                    replies.addAll(processCommandFrame(msg, body));
//                    break;
//                case ProtocolConstants.BODY_SETTING:
//                    replies.addAll(processSettingFrame(msg, body));
//                    break;
//                case ProtocolConstants.BODY_EVENT:
//                    replies.addAll(processEventFrame(msg, body));
//                    break;
//                case ProtocolConstants.BODY_SESSION:
//                    replies.addAll(processSessionFrame(msg, body));
//                    break;
                case ProtocolConstants.BODY_DATA:
                    replies.addAll(processEventFrame(msg, body));
                    System.out.println("8>>>>>>messageModel is not null:"+messageModel);
                    processData(messageModel);
                    break;
            }
        }

        return replies;
    }


    /**
     *
     * @param msg
     * @param body
     * @return
     */
    private List<Frame> processEventFrame(Frame msg, BaseBody body) {
        List<Frame> replies = new ArrayList<Frame>();
        return replies;
    }


    private Header createReplyFrameHeaderByFrame(Frame msg) {
        Header header = new Header();
        header.setVersion(msg.getHeader().getVersion());
        header.setId(msg.getHeader().getId());
        header.setSenderId(msg.getHeader().getSenderId());
        header.setCenterId(1);
        header.setTimestamp(System.currentTimeMillis());
//		header.setTotalLength(83);

        return header;
    }



}
